package com.karolak.stock.data;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.karolak.math.MathUtils;
import com.karolak.stock.indicator.Indicator;

public class DataManager {

	private int historySize;

	private Map<String, List<Double>> data;
	private Map<String, List<String>> chartSeries;
	private Map<String, Indicator> indicators;

	public DataManager(int historySize) {
		this.historySize = historySize;
		this.data = new HashMap<String, List<Double>>();
		this.chartSeries = new LinkedHashMap<String, List<String>>();
		this.indicators = new LinkedHashMap<String, Indicator>();
		init();
	}

	public void init() {
	}

	// === //
	public void update(double x) {
		for (String seriesName : getSeries()) {
			update(seriesName, x);
		}
	}

	public void update(String seriesName, double x) {
		List<Double> data = getData(seriesName);
		double last = calculate(seriesName, x);
		data.add(last);
		while (data.size() > getHistorySize()) {
			data.remove(0);
		}
	}

	public void set(String seriesName, double x) {
		List<Double> data = getData(seriesName);
		double last = x;
		data.add(last);
		while (data.size() > getHistorySize()) {
			data.remove(0);
		}
	}

	public double calculate(String seriesName, double x) {
		return getIndicator(seriesName).execute(x);
	}

	// === add === //
	public <T extends Indicator> T addSeries(T indicator) {
		return addSeries(indicator, null);
	}

	public <T extends Indicator> T addSeries(T indicator, String chartName) {
		getIndicators().put(indicator.getName(), indicator);
		if (chartName == null) {
			return indicator;
		}
		List<String> chartSeriesList = getChartSeries().get(chartName);
		if (chartSeriesList == null) {
			chartSeriesList = new ArrayList<String>();
			getChartSeries().put(chartName, chartSeriesList);
		}
		if (!chartSeriesList.contains(indicator.getName())) {
			chartSeriesList.add(indicator.getName());
		}
		return indicator;
	}
	
	public void addDisplayer(String name, String chartName) {
		List<String> chartSeriesList = getChartSeries().get(chartName);
		if (chartSeriesList == null) {
			chartSeriesList = new ArrayList<String>();
			getChartSeries().put(chartName, chartSeriesList);
		}
		if (!chartSeriesList.contains(name)) {
			chartSeriesList.add(name);
		}
	}	
	

	// === get/set === //
	protected Indicator getIndicator(String seriesName) {
		return getIndicators().get(seriesName);
	}

	public List<String> getSeries() {
		List<String> list = new ArrayList<String>();
		list.addAll(getIndicators().keySet());
		return new ArrayList<String>(list);
	}

	public double getLast(String seriesName) {
		return getLast(seriesName, 0);
	}

	public double getLast(String seriesName, int ix) {
		List<Double> data = getData(seriesName);
		if (data.isEmpty()) {
			return 0;
		}
		if (data.size() > ix) {
			return data.get(data.size() - 1 - ix);
		}
		return 0;
	}

	public double[] getSeries(String seriesName) {
		return MathUtils.toArray(getData(seriesName));
	}

	public double[] getSeries(String seriesName, int size) {
		return MathUtils.toArray(getData(seriesName, size));
	}

	// === get/set === //
	protected List<Double> getData(String seriesName) {
		List<Double> data = getData().get(seriesName);
		if (data == null) {
			data = new ArrayList<Double>(getHistorySize());
		}
		getData().put(seriesName, data);
		return data;
	}

	public List<Double> getData(String seriesName, int size) {
		List<Double> data = getData(seriesName);
		List<Double> subList = new ArrayList<Double>();
		if (data.isEmpty()) {
			return subList;
		}
		int dataSize = data.size();
		for (int i = 0; i < size && i < dataSize; i++) {
			subList.add(data.get(dataSize - 1 - i));
		}
		Collections.reverse(subList);
		return subList;
	}

	public int getSize(String seriesName) {
		return getData(seriesName).size();
	}

	protected Map<String, List<Double>> getData() {
		return data;
	}

	public Map<String, List<String>> getChartSeries() {
		return chartSeries;
	}

	protected int getHistorySize() {
		return historySize;
	}

	protected void setHistorySize(int historySize) {
		this.historySize = historySize;
	}

	public Map<String, Indicator> getIndicators() {
		return indicators;
	}
}
